<html>
 <head>
  <meta charset="utf-8"/>
  <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"/>
  <title>
   从底层理解Python的执行  | 数螺 | NAUT IDEA
  </title>
  <link href="http://cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" rel="stylesheet"/>
  <link href="http://cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"/>
  <style type="text/css">
   #xmain img {
                  max-width: 100%;
                  display: block;
                  margin-top: 10px;
                  margin-bottom: 10px;
                }

                #xmain p {
                    line-height:150%;
                    font-size: 16px;
                    margin-top: 20px;
                }

                #xmain h2 {
                    font-size: 24px;
                }

                #xmain h3 {
                    font-size: 20px;
                }

                #xmain h4 {
                    font-size: 18px;
                }


                .header {
	           background-color: #0099ff;
	           color: #ffffff;
	           margin-bottom: 20px;
	        }

	        .header p {
                  margin: 0px;
                  padding: 10px 0;
                  display: inline-block;  
                  vertical-align: middle;
                  font-size: 16px;
               }

               .header a {
                 color: white;
               }

              .header img {
                 height: 25px;
              }
  </style>
  <script src="http://cdn.bootcss.com/jquery/3.0.0/jquery.min.js">
  </script>
  <script src="http://nautstatic-10007657.file.myqcloud.com/static/css/readability.min.js" type="text/javascript">
  </script>
  <script type="text/javascript">
   $(document).ready(function() {
                 var loc = document.location;
                 var uri = {
                  spec: "http://dataunion.org/19079.html",
                  host: "http://dataunion.org",
                  prePath: "http://dataunion.org",
                  scheme: "http",
                  pathBase: "http://dataunion.org/"
                 };
    
                 var documentClone = document.cloneNode(true);
                 var article = new Readability(uri, documentClone).parse();
     
                 document.getElementById("xmain").innerHTML = article.content;
                });
  </script>
  <!-- 1466461376: Accept with keywords: (title(0.25):Python,社区,数盟,底层, topn(0.4):反编译,社区,编译,定义,生成器,数盟,信息,操作码,查找,数据挖掘,堆栈,函数,Python,指令,参数,文章,基础架构,方法,底层,字节,python,偏移量,目标,地址,字符串,行业资讯,代码,列表,例子,分支).-->
 </head>
 <body onload="">
  <div class="header">
   <div class="container">
    <div class="row">
     <div class="col-xs-6 col-sm-6 text-left">
      <a href="/databee">
       <img src="http://nautidea-10007657.cos.myqcloud.com/logo_white.png"/>
      </a>
      <a href="/databee">
       <p>
        数螺
       </p>
      </a>
     </div>
     <div class="hidden-xs col-sm-6 text-right">
      <p>
       致力于数据科学的推广和知识传播
      </p>
     </div>
    </div>
   </div>
  </div>
  <div class="container text-center">
   <h1>
    从底层理解Python的执行
   </h1>
  </div>
  <div class="container" id="xmain">
   ﻿﻿
   <title>
    从底层理解Python的执行 | 数盟社区
   </title>
   <!-- All in One SEO Pack 2.2.7.6.2 by Michael Torbert of Semper Fi Web Design[32,61] -->
   <!-- /all in one seo pack -->
   <!--
<div align="center">
<a href="http://strata.oreilly.com.cn/hadoop-big-data-cn?cmp=mp-data-confreg-home-stcn16_dataunion_pc" target="_blank"><img src="http://dataunion.org/wp-content/uploads/2016/05/stratabj.jpg"/ ></a>
</div>
-->
   <header id="header-web">
    <div class="header-main">
     <hgroup class="logo">
      <h1>
       <a href="http://dataunion.org/" rel="home" title="数盟社区">
        <img src="http://dataunion.org/wp-content/themes/yzipi/images/logo.png"/>
       </a>
      </h1>
     </hgroup>
     <!--logo-->
     <nav class="header-nav">
      <ul class="menu" id="menu-%e4%b8%bb%e8%8f%9c%e5%8d%95">
       <li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-has-children menu-item-71" id="menu-item-71">
        <a href="http://dataunion.org/category/events" title="events">
         活动
        </a>
        <ul class="sub-menu">
         <li class="menu-item menu-item-type-post_type menu-item-object-page menu-item-22457" id="menu-item-22457">
          <a href="http://dataunion.org/2016timeline">
           2016档期
          </a>
         </li>
         <li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-22459" id="menu-item-22459">
          <a href="http://dataunion.org/category/parterc">
           合作会议
          </a>
         </li>
        </ul>
       </li>
       <li class="menu-item menu-item-type-taxonomy menu-item-object-category current-post-ancestor current-menu-parent current-post-parent menu-item-has-children menu-item-20869" id="menu-item-20869">
        <a href="http://dataunion.org/category/tech" title="articles">
         文章
        </a>
        <ul class="sub-menu">
         <li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-20867" id="menu-item-20867">
          <a href="http://dataunion.org/category/tech/base" title="base">
           基础架构
          </a>
         </li>
         <li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-3302" id="menu-item-3302">
          <a href="http://dataunion.org/category/tech/ai" title="ai">
           人工智能
          </a>
         </li>
         <li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-3303" id="menu-item-3303">
          <a href="http://dataunion.org/category/tech/analysis" title="analysis">
           数据分析
          </a>
         </li>
         <li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-21920" id="menu-item-21920">
          <a href="http://dataunion.org/category/tech/dm">
           数据挖掘
          </a>
         </li>
         <li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-3314" id="menu-item-3314">
          <a href="http://dataunion.org/category/tech/viz" title="viz">
           可视化
          </a>
         </li>
         <li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-3305" id="menu-item-3305">
          <a href="http://dataunion.org/category/tech/devl" title="devl">
           编程语言
          </a>
         </li>
        </ul>
       </li>
       <li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-has-children menu-item-20876" id="menu-item-20876">
        <a href="http://dataunion.org/category/industry">
         行业
        </a>
        <ul class="sub-menu">
         <li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-16328" id="menu-item-16328">
          <a href="http://dataunion.org/category/industry/case" title="case">
           行业应用
          </a>
         </li>
         <li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-2112" id="menu-item-2112">
          <a href="http://dataunion.org/category/industry/demo" title="demo">
           Demo展示
          </a>
         </li>
         <li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-21562" id="menu-item-21562">
          <a href="http://dataunion.org/category/industry/news">
           行业资讯
          </a>
         </li>
        </ul>
       </li>
       <li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-311" id="menu-item-311">
        <a href="http://dataunion.org/category/sources" title="sources">
         资源
        </a>
       </li>
       <li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-20870" id="menu-item-20870">
        <a href="http://dataunion.org/category/books" title="book">
         图书
        </a>
       </li>
       <li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-21363" id="menu-item-21363">
        <a href="http://dataunion.org/category/training">
         课程
        </a>
       </li>
       <li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-has-children menu-item-21853" id="menu-item-21853">
        <a href="http://dataunion.org/category/jobs">
         职位
        </a>
        <ul class="sub-menu">
         <li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-22050" id="menu-item-22050">
          <a href="http://dataunion.org/category/career">
           职业规划
          </a>
         </li>
        </ul>
       </li>
      </ul>
     </nav>
     <!--header-nav-->
    </div>
   </header>
   <!--header-web-->
   <div id="main">
    <div id="soutab">
     <form action="http://dataunion.org/" class="search" method="get">
     </form>
    </div>
    <div id="container">
     <nav id="mbx">
      当前位置：
      <a href="http://dataunion.org">
       首页
      </a>
      &gt;
      <a href="http://dataunion.org/category/tech">
       文章
      </a>
      &gt;  正文
     </nav>
     <!--mbx-->
     <article class="content">
      <header align="centre" class="contenttitle">
       <div class="mscc">
        <h1 class="mscctitle">
         <a href="http://dataunion.org/19079.html">
          从底层理解Python的执行
         </a>
        </h1>
        <address class="msccaddress ">
         <em>
          1,249 次阅读 -
         </em>
         <a href="http://dataunion.org/category/tech" rel="category tag">
          文章
         </a>
        </address>
       </div>
      </header>
      <div class="content-text">
       <p>
        出处：
        <a href="http://www.csdn.net/article/2015-05-28/2824795">
         csdn
        </a>
       </p>
       <p>
        下面博文将带你创建一个字节码级别的追踪API以追踪Python的一些内部机制，比如类似YIELDVALUE、YIELDFROM操作码的实现，推式构造列表(List Comprehensions)、生成器表达式(generator expressions)以及其他一些有趣Python的编译。
       </p>
       <hr/>
       <p>
        <b>
         关于译者
        </b>
        ：赵斌，
        <a href="http://www.oneapm.com/" target="_blank">
         OneAPM
        </a>
        工程师，常年使用 Python/Perl 脚本，从事 DevOP、测试开发相关的开发工作。业余热爱看书，喜欢 MOOC。
       </p>
       <hr/>
       <p>
        <b>
         以下为译文
        </b>
       </p>
       <p>
        最近我在学习 Python 的运行模型。我对 Python 的一些内部机制很是好奇，比如 Python 是怎么实现类似 YIELD
        <em>
         VALUE、YIELD
        </em>
        FROM 这样的操作码的；对于 递推式构造列表(List Comprehensions)、生成器表达式(generator expressions)以及其他一些有趣的 Python 特性是怎么编译的；从字节码的层面来看，当异常抛出的时候都发生了什么事情。翻阅 CPython 的代码对于解答这些问题当然是很有帮助的，但我仍然觉得以这样的方式来做的话对于理解字节码的执行和堆栈的变化还是缺少点什么。GDB 是个好选择，但是我懒，而且只想使用一些比较高阶的接口写点 Python 代码来完成这件事。
       </p>
       <p>
        所以呢，我的目标就是创建一个字节码级别的追踪 API，类似 sys.setrace 所提供的那样，但相对而言会有更好的粒度。这充分锻炼了我编写 Python 实现的 C 代码的编码能力。我们所需要的有如下几项，在这篇文章中所用的 Python 版本为 3.5。
       </p>
       <ul>
        <li>
         一个新的 Cpython 解释器操作码
        </li>
        <li>
         一种将操作码注入到 Python 字节码的方法
        </li>
        <li>
         一些用于处理操作码的 Python 代码
        </li>
       </ul>
       <h3>
        一个新的 Cpython 操作码
       </h3>
       <h4>
        新操作码：DEBUG_OP
       </h4>
       <p>
        这个新的操作码 DEBUG_OP 是我第一次尝试写 CPython 实现的 C 代码，我将尽可能的让它保持简单。 我们想要达成的目的是，当我们的操作码被执行的时候我能有一种方式来调用一些 Python 代码。同时，我们也想能够追踪一些与执行上下文有关的数据。我们的操作码会把这些信息当作参数传递给我们的回调函数。通过操作码能辨识出的有用信息如下：
       </p>
       <ul>
        <li>
         堆栈的内容
        </li>
        <li>
         执行 DEBUG_OP 的帧对象信息
        </li>
       </ul>
       <p>
        所以呢，我们的操作码需要做的事情是：
       </p>
       <ul>
        <li>
         找到回调函数
        </li>
        <li>
         创建一个包含堆栈内容的列表
        </li>
        <li>
         调用回调函数，并将包含堆栈内容的列表和当前帧作为参数传递给它
        </li>
       </ul>
       <p>
        听起来挺简单的，现在开始动手吧！
        <strong>
         声明
        </strong>
        ：下面所有的解释说明和代码是经过了大量段错误调试之后总结得到的结论。首先要做的是给操作码定义一个名字和相应的值，因此我们需要在
        <a href="https://github.com/python/cpython/blob/master/Include/opcode.h">
         Include/opcode.h
        </a>
        中添加代码。
       </p>
       <blockquote>
        <div class="dp-highlighter bg_py">
         <div class="bar">
          <div class="tools">
           <b>
            [py]
           </b>
           <p>
           </p>
           <div>
           </div>
          </div>
         </div>
         <ol class="dp-py" start="1">
          <li class="alt">
           /** My own comments begin by
           <span class="string">
            ‘**’
           </span>
           **/
          </li>
          <li class="">
           /** From: Includes/opcode.h **/
          </li>
          <li class="alt">
          </li>
          <li class="">
           /* Instruction opcodes
           <span class="keyword">
            for
           </span>
           compiled code */
          </li>
          <li class="alt">
          </li>
          <li class="">
           /** We just have to define our opcode with a free value
          </li>
          <li class="alt">
           <span class="number">
            0
           </span>
           was the first one I found **/
          </li>
          <li class="">
           <span class="comment">
            #define DEBUG_OP                0
           </span>
          </li>
          <li class="alt">
          </li>
          <li class="">
           <span class="comment">
            #define POP_TOP                 1
           </span>
          </li>
          <li class="alt">
           <span class="comment">
            #define ROT_TWO                 2
           </span>
          </li>
          <li class="">
           <span class="comment">
            #define ROT_THREE               3
           </span>
          </li>
         </ol>
        </div>
       </blockquote>
       <p>
        这部分工作就完成了，现在我们去编写操作码真正干活的代码。
       </p>
       <h4>
        实现 DEBUG_OP
       </h4>
       <p>
        在考虑如何实现
        <code>
         DEBUG_OP
        </code>
        之前我们需要了解的是
        <code>
         DEBUG_OP
        </code>
        提供的接口将长什么样。 拥有一个可以调用其他代码的新操作码是相当酷眩的，但是究竟它将调用哪些代码捏？这个操作码如何找到回调函数的捏？我选择了一种最简单的方法：在帧的全局区域写死函数名。那么问题就变成了，我该怎么从字典中找到一个固定的 C 字符串？为了回答这个问题我们来看看在 Python 的 main loop 中使用到的和上下文管理相关的标识符
        <code>
         __enter__
        </code>
        和
        <code>
         __exit__。
        </code>
       </p>
       <p>
        我们可以看到这两标识符被使用在操作码
        <code>
         SETUP_WITH
        </code>
        中：
       </p>
       <blockquote>
        <div class="dp-highlighter bg_py">
         <div class="bar">
          <div class="tools">
           <b>
            [py]
           </b>
          </div>
         </div>
         <ol class="dp-py" start="1">
          <li class="alt">
           /** From: Python/ceval.c **/
          </li>
          <li class="">
           TARGET(SETUP_WITH) {
          </li>
          <li class="alt">
           _Py_IDENTIFIER(__exit__);
          </li>
          <li class="">
           _Py_IDENTIFIER(__enter__);
          </li>
          <li class="alt">
           PyObject *mgr = TOP();
          </li>
          <li class="">
           PyObject *exit = special_lookup(mgr, &amp;PyId___exit__), *enter;
          </li>
          <li class="alt">
           PyObject *res;
          </li>
         </ol>
        </div>
       </blockquote>
       <p>
        现在，看一眼宏
        <code>
         _Py_IDENTIFIER
        </code>
        的定义
       </p>
       <blockquote>
        <div class="dp-highlighter bg_py">
         <div class="bar">
          <div class="tools">
           <b>
            [py]
           </b>
          </div>
         </div>
         <ol class="dp-py" start="1">
          <li class="alt">
           /** From: Include/object.h **/
          </li>
          <li class="">
          </li>
          <li class="alt">
           /********************* String Literals ****************************************/
          </li>
          <li class="">
           /* This structure helps managing static strings. The basic usage goes like this:
          </li>
          <li class="alt">
           Instead of doing
          </li>
          <li class="">
          </li>
          <li class="alt">
           r = PyObject_CallMethod(o,
           <span class="string">
            “foo”
           </span>
           ,
           <span class="string">
            “args”
           </span>
           , …);
          </li>
          <li class="">
          </li>
          <li class="alt">
           do
          </li>
          <li class="">
          </li>
          <li class="alt">
           _Py_IDENTIFIER(foo);
          </li>
          <li class="">
           …
          </li>
          <li class="alt">
           r = _PyObject_CallMethodId(o, &amp;PyId_foo,
           <span class="string">
            “args”
           </span>
           , …);
          </li>
          <li class="">
          </li>
          <li class="alt">
           PyId_foo
           <span class="keyword">
            is
           </span>
           a static variable, either on block level
           <span class="keyword">
            or
           </span>
           file level. On first
          </li>
          <li class="">
           usage, the string
           <span class="string">
            “foo”
           </span>
           <span class="keyword">
            is
           </span>
           interned,
           <span class="keyword">
            and
           </span>
           the structures are linked. On interpreter
          </li>
          <li class="alt">
           shutdown, all strings are released (through _PyUnicode_ClearStaticStrings).
          </li>
          <li class="">
          </li>
          <li class="alt">
           Alternatively, _Py_static_string allows to choose the variable name.
          </li>
          <li class="">
           _PyUnicode_FromId returns a borrowed reference to the interned string.
          </li>
          <li class="alt">
           _PyObject_{Get,Set,Has}AttrId are __getattr__ versions using _Py_Identifier*.
          </li>
          <li class="">
           */
          </li>
          <li class="alt">
           typedef struct _Py_Identifier {
          </li>
          <li class="">
           struct _Py_Identifier *next;
          </li>
          <li class="alt">
           const char* string;
          </li>
          <li class="">
           PyObject *object;
          </li>
          <li class="alt">
           } _Py_Identifier;
          </li>
          <li class="">
          </li>
          <li class="alt">
           <span class="comment">
            #define _Py_static_string_init(value) { 0, value, 0 }
           </span>
          </li>
          <li class="">
           <span class="comment">
            #define _Py_static_string(varname, value)  static _Py_Identifier varname = _Py_static_string_init(value)
           </span>
          </li>
          <li class="alt">
           <span class="comment">
            #define _Py_IDENTIFIER(varname) _Py_static_string(PyId_##varname, #varname)
           </span>
          </li>
         </ol>
        </div>
       </blockquote>
       <p>
        嗯，注释部分已经说明得很清楚了。通过一番查找，我们发现了可以用来从字典找固定字符串的函数
        <code>
         _PyDict_GetItemId
        </code>
        ，所以我们操作码的查找部分的代码就是长这样滴。
       </p>
       <blockquote>
        <div class="dp-highlighter bg_py">
         <div class="bar">
          <div class="tools">
           <b>
            [py]
           </b>
          </div>
         </div>
         <ol class="dp-py" start="1">
          <li class="alt">
           /** Our callback function will be named op_target **/
          </li>
          <li class="">
           PyObject *target = NULL;
          </li>
          <li class="alt">
           _Py_IDENTIFIER(op_target);
          </li>
          <li class="">
           target = _PyDict_GetItemId(f-&gt;f_globals, &amp;PyId_op_target);
          </li>
          <li class="alt">
           <span class="keyword">
            if
           </span>
           (target == NULL &amp;&amp; _PyErr_OCCURRED()) {
          </li>
          <li class="">
           <span class="keyword">
            if
           </span>
           (!PyErr_ExceptionMatches(PyExc_KeyError))
          </li>
          <li class="alt">
           goto error;
          </li>
          <li class="">
           PyErr_Clear();
          </li>
          <li class="alt">
           DISPATCH();
          </li>
          <li class="">
           }
          </li>
         </ol>
        </div>
       </blockquote>
       <p>
        为了方便理解，对这一段代码做一些说明：
       </p>
       <ul>
        <li>
         <code>
          f
         </code>
         是当前的帧，
         <code>
          f-&gt;f_globals
         </code>
         是它的全局区域
        </li>
        <li>
         如果我们没有找到
         <code>
          op_target
         </code>
         ，我们将会检查这个异常是不是
         <code>
          KeyError
         </code>
        </li>
        <li>
         <code>
          goto error;
         </code>
         是一种在 main loop 中抛出异常的方法
        </li>
        <li>
         <code>
          PyErr_Clear()
         </code>
         抑制了当前异常的抛出，而
         <code>
          DISPATCH()
         </code>
         触发了下一个操作码的执行
        </li>
       </ul>
       <p>
        下一步就是收集我们想要的堆栈信息。
       </p>
       <blockquote>
        <div class="dp-highlighter bg_py">
         <div class="bar">
          <div class="tools">
           <b>
            [py]
           </b>
          </div>
         </div>
         <ol class="dp-py" start="1">
          <li class="alt">
           /** This code create a list with all the values on the current stack **/
          </li>
          <li class="">
           PyObject *value = PyList_New(
           <span class="number">
            0
           </span>
           );
          </li>
          <li class="alt">
           <span class="keyword">
            for
           </span>
           (i =
           <span class="number">
            1
           </span>
           ; i &lt;= STACK_LEVEL(); i++) {
          </li>
          <li class="">
           tmp = PEEK(i);
          </li>
          <li class="alt">
           <span class="keyword">
            if
           </span>
           (tmp == NULL) {
          </li>
          <li class="">
           tmp = Py_None;
          </li>
          <li class="alt">
           }
          </li>
          <li class="">
           PyList_Append(value, tmp);
          </li>
          <li class="alt">
           }
          </li>
         </ol>
        </div>
       </blockquote>
       <p>
        最后一步就是调用我们的回调函数！我们用
        <code>
         call_function
        </code>
        来搞定这件事，我们通过研究操作码
        <code>
         CALL_FUNCTION
        </code>
        的实现来学习怎么使用
        <code>
         call_function
        </code>
        。
       </p>
       <blockquote>
        <div class="dp-highlighter bg_py">
         <div class="bar">
          <div class="tools">
           <b>
            [py]
           </b>
          </div>
         </div>
         <ol class="dp-py" start="1">
          <li class="alt">
           /** From: Python/ceval.c **/
          </li>
          <li class="">
           TARGET(CALL_FUNCTION) {
          </li>
          <li class="alt">
           PyObject **sp, *res;
          </li>
          <li class="">
           /** stack_pointer
           <span class="keyword">
            is
           </span>
           a local of the main loop.
          </li>
          <li class="alt">
           It’s the pointer to the stacktop of our frame **/
          </li>
          <li class="">
           sp = stack_pointer;
          </li>
          <li class="alt">
           res = call_function(&amp;sp, oparg);
          </li>
          <li class="">
           /** call_function handles the args it consummed on the stack
           <span class="keyword">
            for
           </span>
           us **/
          </li>
          <li class="alt">
           stack_pointer = sp;
          </li>
          <li class="">
           PUSH(res);
          </li>
          <li class="alt">
           /** Standard exception handling **/
          </li>
          <li class="">
           <span class="keyword">
            if
           </span>
           (res == NULL)
          </li>
          <li class="alt">
           goto error;
          </li>
          <li class="">
           DISPATCH();
          </li>
          <li class="alt">
           }
          </li>
         </ol>
        </div>
       </blockquote>
       <p>
        有了上面这些信息，我们终于可以捣鼓出一个操作码
        <code>
         DEBUG_OP
        </code>
        的草稿了：
       </p>
       <blockquote>
        <div class="dp-highlighter bg_py">
         <div class="bar">
          <div class="tools">
           <b>
            [py]
           </b>
          </div>
         </div>
         <ol class="dp-py" start="1">
          <li class="alt">
           TARGET(DEBUG_OP) {
          </li>
          <li class="">
           PyObject *value = NULL;
          </li>
          <li class="alt">
           PyObject *target = NULL;
          </li>
          <li class="">
           PyObject *res = NULL;
          </li>
          <li class="alt">
           PyObject **sp = NULL;
          </li>
          <li class="">
           PyObject *tmp;
          </li>
          <li class="alt">
           int i;
          </li>
          <li class="">
           _Py_IDENTIFIER(op_target);
          </li>
          <li class="alt">
          </li>
          <li class="">
           target = _PyDict_GetItemId(f-&gt;f_globals, &amp;PyId_op_target);
          </li>
          <li class="alt">
           <span class="keyword">
            if
           </span>
           (target == NULL &amp;&amp; _PyErr_OCCURRED()) {
          </li>
          <li class="">
           <span class="keyword">
            if
           </span>
           (!PyErr_ExceptionMatches(PyExc_KeyError))
          </li>
          <li class="alt">
           goto error;
          </li>
          <li class="">
           PyErr_Clear();
          </li>
          <li class="alt">
           DISPATCH();
          </li>
          <li class="">
           }
          </li>
          <li class="alt">
           value = PyList_New(
           <span class="number">
            0
           </span>
           );
          </li>
          <li class="">
           Py_INCREF(target);
          </li>
          <li class="alt">
           <span class="keyword">
            for
           </span>
           (i =
           <span class="number">
            1
           </span>
           ; i &lt;= STACK_LEVEL(); i++) {
          </li>
          <li class="">
           tmp = PEEK(i);
          </li>
          <li class="alt">
           <span class="keyword">
            if
           </span>
           (tmp == NULL)
          </li>
          <li class="">
           tmp = Py_None;
          </li>
          <li class="alt">
           PyList_Append(value, tmp);
          </li>
          <li class="">
           }
          </li>
          <li class="alt">
          </li>
          <li class="">
           PUSH(target);
          </li>
          <li class="alt">
           PUSH(value);
          </li>
          <li class="">
           Py_INCREF(f);
          </li>
          <li class="alt">
           PUSH(f);
          </li>
          <li class="">
           sp = stack_pointer;
          </li>
          <li class="alt">
           res = call_function(&amp;sp,
           <span class="number">
            2
           </span>
           );
          </li>
          <li class="">
           stack_pointer = sp;
          </li>
          <li class="alt">
           <span class="keyword">
            if
           </span>
           (res == NULL)
          </li>
          <li class="">
           goto error;
          </li>
          <li class="alt">
           Py_DECREF(res);
          </li>
          <li class="">
           DISPATCH();
          </li>
          <li class="alt">
           }
          </li>
         </ol>
        </div>
       </blockquote>
       <p>
        在编写 CPython 实现的 C 代码方面我确实没有什么经验，有可能我漏掉了些细节。如果您有什么建议还请您纠正，我期待您的反馈。
       </p>
       <p>
        编译它，成了！
       </p>
       <p>
        一切看起来很顺利，但是当我们尝试去使用我们定义的操作码
        <code>
         DEBUG_OP
        </code>
        的时候却失败了。自从 2008 年之后，Python 使用预先写好的
        <a href="http://bugs.python.org/issue4753">
         goto
        </a>
        (你也可以从
        <a href="http://eli.thegreenplace.net/2012/07/12/computed-goto-for-efficient-dispatch-tables">
         这里
        </a>
        获取更多的讯息)。故，我们需要更新下 goto jump table，我们在 Python/opcode_targets.h 中做如下修改。
       </p>
       <blockquote>
        <div class="dp-highlighter bg_py">
         <div class="bar">
          <div class="tools">
           <b>
            [py]
           </b>
          </div>
         </div>
         <ol class="dp-py" start="1">
          <li class="alt">
           /** From: Python/opcode_targets.h **/
          </li>
          <li class="">
           /** Easy change since DEBUG_OP
           <span class="keyword">
            is
           </span>
           the opcode number
           <span class="number">
            1
           </span>
           **/
          </li>
          <li class="alt">
           static void *opcode_targets[
           <span class="number">
            256
           </span>
           ] = {
          </li>
          <li class="">
           //&amp;&amp;_unknown_opcode,
          </li>
          <li class="alt">
           &amp;&amp;TARGET_DEBUG_OP,
          </li>
          <li class="">
           &amp;&amp;TARGET_POP_TOP,
          </li>
          <li class="alt">
           /** … **/
          </li>
         </ol>
        </div>
       </blockquote>
       <p>
        这就完事了，我们现在就有了一个可以工作的新操作码。唯一的问题就是这货虽然存在，但是没有被人调用过。接下来，我们将
        <code>
         DEBUG_OP
        </code>
        注入到函数的字节码中。
       </p>
       <h3>
        在 Python 字节码中注入操作码 DEBUG_OP
       </h3>
       <p>
        有很多方式可以在 Python 字节码中注入新的操作码：
       </p>
       <ul>
        <li>
         使用 peephole optimizer，
         <a href="http://blog.quarkslab.com/building-an-obfuscated-python-interpreter-we-need-more-opcodes.html">
          Quarkslab
         </a>
         就是这么干的
        </li>
        <li>
         在生成字节码的代码中动些手脚
        </li>
        <li>
         在运行时直接修改函数的字节码(这就是我们将要干的事儿)
        </li>
       </ul>
       <p>
        为了创造出一个新操作码，有了上面的那一堆 C 代码就够了。现在让我们回到原点，开始理解奇怪甚至神奇的 Python！
       </p>
       <p>
        我们将要做的事儿有：
       </p>
       <ul>
        <li>
         得到我们想要追踪函数的 code object
        </li>
        <li>
         重写字节码来注入
         <code>
          DEBUG_OP
         </code>
        </li>
        <li>
         将新生成的 code object 替换回去
        </li>
       </ul>
       <h4>
        和 code object 有关的小贴士
       </h4>
       <p>
        如果你从没听说过 code object，这里有一个简单的
        <a href="http://blog.hakril.net/articles/0-understanding-python-by-breaking-it.html">
         介绍
        </a>
        网路上也有一些相关的
        <a href="https://docs.python.org/3.4/reference/datamodel.html">
         文档
        </a>
        可供查阅,可以直接
        <code>
         Ctrl+F
        </code>
        查找 code object
       </p>
       <p>
        还有一件事情需要注意的是在这篇文章所指的环境中 code object 是
        <strong>
         不可变
        </strong>
        的：
       </p>
       <blockquote>
        <div class="dp-highlighter bg_py">
         <div class="bar">
          <div class="tools">
           <b>
            [py]
           </b>
          </div>
         </div>
         <ol class="dp-py" start="1">
          <li class="alt">
           Python
           <span class="number">
            3.4
           </span>
           .
           <span class="number">
            2
           </span>
           (default, Oct
           <span class="number">
            8
           </span>
           <span class="number">
            2014
           </span>
           ,
           <span class="number">
            10
           </span>
           :
           <span class="number">
            45
           </span>
           :
           <span class="number">
            20
           </span>
           )
          </li>
          <li class="">
           [GCC
           <span class="number">
            4.9
           </span>
           .
           <span class="number">
            1
           </span>
           ] on linux
          </li>
          <li class="alt">
           Type
           <span class="string">
            “help”
           </span>
           ,
           <span class="string">
            “copyright”
           </span>
           ,
           <span class="string">
            “credits”
           </span>
           <span class="keyword">
            or
           </span>
           <span class="string">
            “license”
           </span>
           <span class="keyword">
            for
           </span>
           more information.
          </li>
          <li class="">
           &gt;&gt;&gt; x =
           <span class="keyword">
            lambda
           </span>
           y :
           <span class="number">
            2
           </span>
          </li>
          <li class="alt">
           &gt;&gt;&gt; x.__code__
          </li>
          <li class="">
           &lt;code object &lt;
           <span class="keyword">
            lambda
           </span>
           &gt; at
           <span class="number">
            0x7f481fd88390
           </span>
           , file
           <span class="string">
            “&lt;stdin&gt;”
           </span>
           , line
           <span class="number">
            1
           </span>
           &gt;
          </li>
          <li class="alt">
           &gt;&gt;&gt; x.__code__.co_name
          </li>
          <li class="">
           <span class="string">
            ‘&lt;lambda&gt;’
           </span>
          </li>
          <li class="alt">
           &gt;&gt;&gt; x.__code__.co_name =
           <span class="string">
            ‘truc’
           </span>
          </li>
          <li class="">
           Traceback (most recent call last):
          </li>
          <li class="alt">
           File
           <span class="string">
            “&lt;stdin&gt;”
           </span>
           , line
           <span class="number">
            1
           </span>
           ,
           <span class="keyword">
            in
           </span>
           &lt;module&gt;
          </li>
          <li class="">
           AttributeError: readonly attribute
          </li>
          <li class="alt">
           &gt;&gt;&gt; x.__code__.co_consts = (
           <span class="string">
            ‘truc’
           </span>
           ,)
          </li>
          <li class="">
           Traceback (most recent call last):
          </li>
          <li class="alt">
           File
           <span class="string">
            “&lt;stdin&gt;”
           </span>
           , line
           <span class="number">
            1
           </span>
           ,
           <span class="keyword">
            in
           </span>
           &lt;module&gt;
          </li>
          <li class="">
           AttributeError: readonly attribute
          </li>
         </ol>
        </div>
       </blockquote>
       <p>
        但是不用担心，我们将会找到方法绕过这个问题的。
       </p>
       <h3>
        使用的工具
       </h3>
       <p>
        为了修改字节码我们需要一些工具：
       </p>
       <ul>
        <li>
         <a href="https://docs.python.org/3.4/library/dis.html">
          dis
         </a>
         模块用来反编译和分析字节码
        </li>
        <li>
         <code>
          dis.Bytecode
         </code>
         Python 3.4 新增的一个特性，对于反编译和分析字节码特别有用
        </li>
        <li>
         一个能够简单修改 code object 的方法
        </li>
       </ul>
       <p>
        用
        <code>
         dis.Bytecode
        </code>
        反编译 code bject 能告诉我们一些有关操作码、参数和上下文的信息。
       </p>
       <blockquote>
        <div class="dp-highlighter bg_py">
         <div class="bar">
          <div class="tools">
           <b>
            [py]
           </b>
          </div>
         </div>
         <ol class="dp-py" start="1">
          <li class="alt">
           <span class="comment">
            # Python3.4
           </span>
          </li>
          <li class="">
           &gt;&gt;&gt;
           <span class="keyword">
            import
           </span>
           dis
          </li>
          <li class="alt">
           &gt;&gt;&gt; f =
           <span class="keyword">
            lambda
           </span>
           x: x +
           <span class="number">
            3
           </span>
          </li>
          <li class="">
           &gt;&gt;&gt;
           <span class="keyword">
            for
           </span>
           i
           <span class="keyword">
            in
           </span>
           dis.Bytecode(f.__code__):
           <span class="keyword">
            print
           </span>
           (i)
          </li>
          <li class="alt">
           …
          </li>
          <li class="">
           Instruction(opname=
           <span class="string">
            ‘LOAD_FAST’
           </span>
           , opcode=
           <span class="number">
            124
           </span>
           , arg=
           <span class="number">
            0
           </span>
           , argval=
           <span class="string">
            ‘x’
           </span>
           , argrepr=
           <span class="string">
            ‘x’
           </span>
           , offset=
           <span class="number">
            0
           </span>
           , starts_line=
           <span class="number">
            1
           </span>
           , is_jump_target=
           <span class="special">
            False
           </span>
           )
          </li>
          <li class="alt">
           Instruction(opname=
           <span class="string">
            ‘LOAD_CONST’
           </span>
           , opcode=
           <span class="number">
            100
           </span>
           , arg=
           <span class="number">
            1
           </span>
           , argval=
           <span class="number">
            3
           </span>
           , argrepr=
           <span class="string">
            ‘3’
           </span>
           , offset=
           <span class="number">
            3
           </span>
           , starts_line=
           <span class="special">
            None
           </span>
           , is_jump_target=
           <span class="special">
            False
           </span>
           )
          </li>
          <li class="">
           Instruction(opname=
           <span class="string">
            ‘BINARY_ADD’
           </span>
           , opcode=
           <span class="number">
            23
           </span>
           , arg=
           <span class="special">
            None
           </span>
           , argval=
           <span class="special">
            None
           </span>
           , argrepr=
           <span class="string">
            ”
           </span>
           , offset=
           <span class="number">
            6
           </span>
           , starts_line=
           <span class="special">
            None
           </span>
           , is_jump_target=
           <span class="special">
            False
           </span>
           )
          </li>
          <li class="alt">
           Instruction(opname=
           <span class="string">
            ‘RETURN_VALUE’
           </span>
           , opcode=
           <span class="number">
            83
           </span>
           , arg=
           <span class="special">
            None
           </span>
           , argval=
           <span class="special">
            None
           </span>
           , argrepr=
           <span class="string">
            ”
           </span>
           , offset=
           <span class="number">
            7
           </span>
           , starts_line=
           <span class="special">
            None
           </span>
           , is_jump_target=
           <span class="special">
            False
           </span>
           )
          </li>
         </ol>
        </div>
       </blockquote>
       <p>
        为了能够修改 code object，我定义了一个很小的类用来复制 code object，同时能够按我们的需求修改相应的值，然后重新生成一个新的 code object。
       </p>
       <blockquote>
        <div class="dp-highlighter bg_py">
         <div class="bar">
          <div class="tools">
           <b>
            [py]
           </b>
          </div>
         </div>
         <ol class="dp-py" start="1">
          <li class="alt">
           <span class="keyword">
            class
           </span>
           MutableCodeObject(object):
          </li>
          <li class="">
           args_name = (
           <span class="string">
            “co_argcount”
           </span>
           ,
           <span class="string">
            “co_kwonlyargcount”
           </span>
           ,
           <span class="string">
            “co_nlocals”
           </span>
           ,
           <span class="string">
            “co_stacksize”
           </span>
           ,
           <span class="string">
            “co_flags”
           </span>
           ,
           <span class="string">
            “co_code”
           </span>
           ,
          </li>
          <li class="alt">
           <span class="string">
            “co_consts”
           </span>
           ,
           <span class="string">
            “co_names”
           </span>
           ,
           <span class="string">
            “co_varnames”
           </span>
           ,
           <span class="string">
            “co_filename”
           </span>
           ,
           <span class="string">
            “co_name”
           </span>
           ,
           <span class="string">
            “co_firstlineno”
           </span>
           ,
          </li>
          <li class="">
           <span class="string">
            “co_lnotab”
           </span>
           ,
           <span class="string">
            “co_freevars”
           </span>
           ,
           <span class="string">
            “co_cellvars”
           </span>
           )
          </li>
          <li class="alt">
          </li>
          <li class="">
           <span class="keyword">
            def
           </span>
           __init__(
           <span class="special">
            self
           </span>
           , initial_code):
          </li>
          <li class="alt">
           <span class="special">
            self
           </span>
           .initial_code = initial_code
          </li>
          <li class="">
           <span class="keyword">
            for
           </span>
           attr_name
           <span class="keyword">
            in
           </span>
           <span class="special">
            self
           </span>
           .args_name:
          </li>
          <li class="alt">
           attr = getattr(
           <span class="special">
            self
           </span>
           .initial_code, attr_name)
          </li>
          <li class="">
           <span class="keyword">
            if
           </span>
           isinstance(attr, tuple):
          </li>
          <li class="alt">
           attr = list(attr)
          </li>
          <li class="">
           setattr(
           <span class="special">
            self
           </span>
           , attr_name, attr)
          </li>
          <li class="alt">
          </li>
          <li class="">
           <span class="keyword">
            def
           </span>
           get_code(
           <span class="special">
            self
           </span>
           ):
          </li>
          <li class="alt">
           args = []
          </li>
          <li class="">
           <span class="keyword">
            for
           </span>
           attr_name
           <span class="keyword">
            in
           </span>
           <span class="special">
            self
           </span>
           .args_name:
          </li>
          <li class="alt">
           attr = getattr(
           <span class="special">
            self
           </span>
           , attr_name)
          </li>
          <li class="">
           <span class="keyword">
            if
           </span>
           isinstance(attr, list):
          </li>
          <li class="alt">
           attr = tuple(attr)
          </li>
          <li class="">
           args.append(attr)
          </li>
          <li class="alt">
           <span class="keyword">
            return
           </span>
           <span class="special">
            self
           </span>
           .initial_code.__class__(*args)
          </li>
         </ol>
        </div>
       </blockquote>
       <p>
        这个类用起来很方便，解决了上面提到的 code object 不可变的问题。
       </p>
       <blockquote>
        <div class="dp-highlighter bg_py">
         <div class="bar">
          <div class="tools">
           <b>
            [py]
           </b>
          </div>
         </div>
         <ol class="dp-py" start="1">
          <li class="alt">
           &gt;&gt;&gt; x =
           <span class="keyword">
            lambda
           </span>
           y :
           <span class="number">
            2
           </span>
          </li>
          <li class="">
           &gt;&gt;&gt; m = MutableCodeObject(x.__code__)
          </li>
          <li class="alt">
           &gt;&gt;&gt; m
          </li>
          <li class="">
           &lt;new_code.MutableCodeObject object at
           <span class="number">
            0x7f3f0ea546a0
           </span>
           &gt;
          </li>
          <li class="alt">
           &gt;&gt;&gt; m.co_consts
          </li>
          <li class="">
           [
           <span class="special">
            None
           </span>
           ,
           <span class="number">
            2
           </span>
           ]
          </li>
          <li class="alt">
           &gt;&gt;&gt; m.co_consts[
           <span class="number">
            1
           </span>
           ] =
           <span class="string">
            ‘3’
           </span>
          </li>
          <li class="">
           &gt;&gt;&gt; m.co_name =
           <span class="string">
            ‘truc’
           </span>
          </li>
          <li class="alt">
           &gt;&gt;&gt; m.get_code()
          </li>
          <li class="">
           &lt;code object truc at
           <span class="number">
            0x7f3f0ea2bc90
           </span>
           , file
           <span class="string">
            “&lt;stdin&gt;”
           </span>
           , line
           <span class="number">
            1
           </span>
           &gt;
          </li>
         </ol>
        </div>
       </blockquote>
       <h3>
        测试我们的新操作码
       </h3>
       <p>
        我们现在拥有了注入
        <code>
         DEBUG_OP
        </code>
        的所有工具，让我们来验证下我们的实现是否可用。我们将我们的操作码注入到一个最简单的函数中：
       </p>
       <blockquote>
        <div class="dp-highlighter bg_py">
         <div class="bar">
          <div class="tools">
           <b>
            [py]
           </b>
          </div>
         </div>
         <ol class="dp-py" start="1">
          <li class="alt">
           <span class="keyword">
            from
           </span>
           new_code
           <span class="keyword">
            import
           </span>
           MutableCodeObject
          </li>
          <li class="">
          </li>
          <li class="alt">
           <span class="keyword">
            def
           </span>
           op_target(*args):
          </li>
          <li class="">
           <span class="keyword">
            print
           </span>
           (
           <span class="string">
            “WOOT”
           </span>
           )
          </li>
          <li class="alt">
           <span class="keyword">
            print
           </span>
           (
           <span class="string">
            “op_target called with args &lt;{0}&gt;”
           </span>
           .format(args))
          </li>
          <li class="">
          </li>
          <li class="alt">
           <span class="keyword">
            def
           </span>
           nop():
          </li>
          <li class="">
           <span class="keyword">
            pass
           </span>
          </li>
          <li class="alt">
          </li>
          <li class="">
           new_nop_code = MutableCodeObject(nop.__code__)
          </li>
          <li class="alt">
           new_nop_code.co_code = b
           <span class="string">
            “\x00”
           </span>
           + new_nop_code.co_code[
           <span class="number">
            0
           </span>
           :
           <span class="number">
            3
           </span>
           ] + b
           <span class="string">
            “\x00”
           </span>
           + new_nop_code.co_code[-
           <span class="number">
            1
           </span>
           :]
          </li>
          <li class="">
           new_nop_code.co_stacksize +=
           <span class="number">
            3
           </span>
          </li>
          <li class="alt">
          </li>
          <li class="">
           nop.__code__ = new_nop_code.get_code()
          </li>
          <li class="alt">
          </li>
          <li class="">
           <span class="keyword">
            import
           </span>
           dis
          </li>
          <li class="alt">
           dis.dis(nop)
          </li>
          <li class="">
           nop()
          </li>
          <li class="alt">
          </li>
          <li class="">
          </li>
          <li class="alt">
           <span class="comment">
            # Don’t forget that ./python is our custom Python implementing DEBUG_OP
           </span>
          </li>
          <li class="">
           hakril@computer ~/python/CPython3.
           <span class="number">
            5
           </span>
           % ./python proof.py
          </li>
          <li class="alt">
           <span class="number">
            8
           </span>
           <span class="number">
            0
           </span>
           &lt;
           <span class="number">
            0
           </span>
           &gt;
          </li>
          <li class="">
           <span class="number">
            1
           </span>
           LOAD_CONST
           <span class="number">
            0
           </span>
           (
           <span class="special">
            None
           </span>
           )
          </li>
          <li class="alt">
           <span class="number">
            4
           </span>
           &lt;
           <span class="number">
            0
           </span>
           &gt;
          </li>
          <li class="">
           <span class="number">
            5
           </span>
           RETURN_VALUE
          </li>
          <li class="alt">
           WOOT
          </li>
          <li class="">
           op_target called with args &lt;([], &lt;frame object at
           <span class="number">
            0x7fde9eaebdb0
           </span>
           &gt;)&gt;
          </li>
          <li class="alt">
           WOOT
          </li>
          <li class="">
           op_target called with args &lt;([
           <span class="special">
            None
           </span>
           ], &lt;frame object at
           <span class="number">
            0x7fde9eaebdb0
           </span>
           &gt;)&gt;
          </li>
         </ol>
        </div>
       </blockquote>
       <p>
        看起来它成功了！有一行代码需要说明一下
        <code>
         new_nop_code.co_stacksize += 3
        </code>
       </p>
       <ul>
        <li>
         co_stacksize 表示 code object 所需要的堆栈的大小
        </li>
        <li>
         操作码
         <code>
          DEBUG_OP
         </code>
         往堆栈中增加了三项，所以我们需要为这些增加的项预留些空间
        </li>
       </ul>
       <p>
        现在我们可以将我们的操作码注入到每一个 Python 函数中了！
       </p>
       <h3>
        重写字节码
       </h3>
       <p>
        正如我们在上面的例子中所看到的那样，重写 Pyhton 的字节码似乎 so easy。为了在每一个操作码之间注入我们的操作码，我们需要获取每一个操作码的偏移量，然后将我们的操作码注入到这些位置上(把我们操作码注入到参数上是有坏处大大滴)。这些偏移量也很容易获取，使用
        <code>
         dis.Bytecode
        </code>
        ，就像这样 。
       </p>
       <blockquote>
        <div class="dp-highlighter bg_py">
         <div class="bar">
          <div class="tools">
           <b>
            [py]
           </b>
          </div>
         </div>
         <ol class="dp-py" start="1">
          <li class="alt">
           <span class="keyword">
            def
           </span>
           add_debug_op_everywhere(code_obj):
          </li>
          <li class="">
           <span class="comment">
            # We get every instruction offset in the code object
           </span>
          </li>
          <li class="alt">
           offsets = [instr.offset
           <span class="keyword">
            for
           </span>
           instr
           <span class="keyword">
            in
           </span>
           dis.Bytecode(code_obj)]
          </li>
          <li class="">
           <span class="comment">
            # And insert a DEBUG_OP at every offset
           </span>
          </li>
          <li class="alt">
           <span class="keyword">
            return
           </span>
           insert_op_debug_list(code_obj, offsets)
          </li>
          <li class="">
          </li>
          <li class="alt">
           <span class="keyword">
            def
           </span>
           insert_op_debug_list(code, offsets):
          </li>
          <li class="">
           <span class="comment">
            # We insert the DEBUG_OP one by one
           </span>
          </li>
          <li class="alt">
           <span class="keyword">
            for
           </span>
           nb, off
           <span class="keyword">
            in
           </span>
           enumerate(sorted(offsets)):
          </li>
          <li class="">
           <span class="comment">
            # Need to ajust the offsets by the number of opcodes already inserted before
           </span>
          </li>
          <li class="alt">
           <span class="comment">
            # That’s why we sort our offsets!
           </span>
          </li>
          <li class="">
           code = insert_op_debug(code, off + nb)
          </li>
          <li class="alt">
           <span class="keyword">
            return
           </span>
           code
          </li>
          <li class="">
          </li>
          <li class="alt">
           <span class="comment">
            # Last problem: what does insert_op_debug looks like?
           </span>
          </li>
         </ol>
        </div>
       </blockquote>
       <p>
        基于上面的例子，有人可能会想我们的
        <code>
         insert_op_debug
        </code>
        会在指定的偏移量增加一个
        <code>
         "\x00"
        </code>
        ，这尼玛是个坑啊！我们第一个
        <code>
         DEBUG_OP
        </code>
        注入的例子中被注入的函数是没有任何的分支的，为了能够实现完美一个函数注入函数
        <code>
         insert_op_debug
        </code>
        我们需要考虑到存在分支操作码的情况。
       </p>
       <p>
        Python 的分支一共有两种：
       </p>
       <ul>
        <li>
         绝对分支：看起来是类似这样子的
         <code>
          Instruction_Pointer = argument(instruction)
         </code>
        </li>
        <li>
         相对分支：看起来是类似这样子的
         <code>
          Instruction_Pointer += argument(instruction)
         </code>
         <ul>
          <li>
           相对分支总是向前的
          </li>
         </ul>
        </li>
       </ul>
       <p>
        我们希望这些分支在我们插入操作码之后仍然能够正常工作，为此我们需要修改一些指令参数。以下是其逻辑流程：
       </p>
       <ul>
        <li>
         对于每一个在插入偏移量之前的相对分支而言
         <ul>
          <li>
           如果目标地址是严格大于我们的插入偏移量的话，将指令参数增加 1
          </li>
          <li>
           如果相等，则不需要增加 1 就能够在跳转操作和目标地址之间执行我们的操作码
           <code>
            DEBUG_OP
           </code>
          </li>
          <li>
           如果小于，插入我们的操作码的话并不会影响到跳转操作和目标地址之间的距离
          </li>
         </ul>
        </li>
        <li>
         对于 code object 中的每一个绝对分支而言
         <ul>
          <li>
           如果目标地址是严格大于我们的插入偏移量的话，将指令参数增加 1
          </li>
          <li>
           如果相等，那么不需要任何修改，理由和相对分支部分是一样的
          </li>
          <li>
           如果小于，插入我们的操作码的话并不会影响到跳转操作和目标地址之间的距离
          </li>
         </ul>
        </li>
       </ul>
       <p>
        下面是实现：
       </p>
       <blockquote>
        <div class="dp-highlighter bg_py">
         <div class="bar">
          <div class="tools">
           <b>
            [py]
           </b>
          </div>
         </div>
         <ol class="dp-py" start="1">
          <li class="alt">
           <span class="comment">
            # Helper
           </span>
          </li>
          <li class="">
           <span class="keyword">
            def
           </span>
           bytecode_to_string(bytecode):
          </li>
          <li class="alt">
           <span class="keyword">
            if
           </span>
           bytecode.arg
           <span class="keyword">
            is
           </span>
           <span class="keyword">
            not
           </span>
           <span class="special">
            None
           </span>
           :
          </li>
          <li class="">
           <span class="keyword">
            return
           </span>
           struct.pack(
           <span class="string">
            “&lt;Bh”
           </span>
           , bytecode.opcode, bytecode.arg)
          </li>
          <li class="alt">
           <span class="keyword">
            return
           </span>
           struct.pack(
           <span class="string">
            “&lt;B”
           </span>
           , bytecode.opcode)
          </li>
          <li class="">
          </li>
          <li class="alt">
           <span class="comment">
            # Dummy class for bytecode_to_string
           </span>
          </li>
          <li class="">
           <span class="keyword">
            class
           </span>
           DummyInstr:
          </li>
          <li class="alt">
           <span class="keyword">
            def
           </span>
           __init__(
           <span class="special">
            self
           </span>
           , opcode, arg):
          </li>
          <li class="">
           <span class="special">
            self
           </span>
           .opcode = opcode
          </li>
          <li class="alt">
           <span class="special">
            self
           </span>
           .arg = arg
          </li>
          <li class="">
          </li>
          <li class="alt">
           <span class="keyword">
            def
           </span>
           insert_op_debug(code, offset):
          </li>
          <li class="">
           opcode_jump_rel = [
           <span class="string">
            ‘FOR_ITER’
           </span>
           ,
           <span class="string">
            ‘JUMP_FORWARD’
           </span>
           ,
           <span class="string">
            ‘SETUP_LOOP’
           </span>
           ,
           <span class="string">
            ‘SETUP_WITH’
           </span>
           ,
           <span class="string">
            ‘SETUP_EXCEPT’
           </span>
           ,
           <span class="string">
            ‘SETUP_FINALLY’
           </span>
           ]
          </li>
          <li class="alt">
           opcode_jump_abs = [
           <span class="string">
            ‘POP_JUMP_IF_TRUE’
           </span>
           ,
           <span class="string">
            ‘POP_JUMP_IF_FALSE’
           </span>
           ,
           <span class="string">
            ‘JUMP_ABSOLUTE’
           </span>
           ]
          </li>
          <li class="">
           res_codestring = b””
          </li>
          <li class="alt">
           inserted =
           <span class="special">
            False
           </span>
          </li>
          <li class="">
           <span class="keyword">
            for
           </span>
           instr
           <span class="keyword">
            in
           </span>
           dis.Bytecode(code):
          </li>
          <li class="alt">
           <span class="keyword">
            if
           </span>
           instr.offset == offset:
          </li>
          <li class="">
           res_codestring += b
           <span class="string">
            “\x00”
           </span>
          </li>
          <li class="alt">
           inserted =
           <span class="special">
            True
           </span>
          </li>
          <li class="">
           <span class="keyword">
            if
           </span>
           instr.opname
           <span class="keyword">
            in
           </span>
           opcode_jump_rel
           <span class="keyword">
            and
           </span>
           <span class="keyword">
            not
           </span>
           inserted:
           <span class="comment">
            #relative jump are always forward
           </span>
          </li>
          <li class="alt">
           <span class="keyword">
            if
           </span>
           offset &lt; instr.offset +
           <span class="number">
            3
           </span>
           + instr.arg:
           <span class="comment">
            # inserted beetwen jump and dest: add 1 to dest (3 for size)
           </span>
          </li>
          <li class="">
           <span class="comment">
            #If equal: jump on DEBUG_OP to get info before exec instr
           </span>
          </li>
          <li class="alt">
           res_codestring += bytecode_to_string(DummyInstr(instr.opcode, instr.arg +
           <span class="number">
            1
           </span>
           ))
          </li>
          <li class="">
           <span class="keyword">
            continue
           </span>
          </li>
          <li class="alt">
           <span class="keyword">
            if
           </span>
           instr.opname
           <span class="keyword">
            in
           </span>
           opcode_jump_abs:
          </li>
          <li class="">
           <span class="keyword">
            if
           </span>
           instr.arg &gt; offset:
          </li>
          <li class="alt">
           res_codestring += bytecode_to_string(DummyInstr(instr.opcode, instr.arg +
           <span class="number">
            1
           </span>
           ))
          </li>
          <li class="">
           <span class="keyword">
            continue
           </span>
          </li>
          <li class="alt">
           res_codestring += bytecode_to_string(instr)
          </li>
          <li class="">
           <span class="comment">
            # replace_bytecode just replaces the original code co_code
           </span>
          </li>
          <li class="alt">
           <span class="keyword">
            return
           </span>
           replace_bytecode(code, res_codestring)
          </li>
         </ol>
        </div>
       </blockquote>
       <p>
        让我们看一下效果如何：
       </p>
       <blockquote>
        <div class="dp-highlighter bg_py">
         <div class="bar">
          <div class="tools">
           <b>
            [py]
           </b>
          </div>
         </div>
         <ol class="dp-py" start="1">
          <li class="alt">
           &gt;&gt;&gt;
           <span class="keyword">
            def
           </span>
           lol(x):
          </li>
          <li class="">
           …
           <span class="keyword">
            for
           </span>
           i
           <span class="keyword">
            in
           </span>
           range(
           <span class="number">
            10
           </span>
           ):
          </li>
          <li class="alt">
           …
           <span class="keyword">
            if
           </span>
           x == i:
          </li>
          <li class="">
           …
           <span class="keyword">
            break
           </span>
          </li>
          <li class="alt">
          </li>
          <li class="">
           &gt;&gt;&gt; dis.dis(lol)
          </li>
          <li class="alt">
           <span class="number">
            101
           </span>
           <span class="number">
            0
           </span>
           SETUP_LOOP
           <span class="number">
            36
           </span>
           (to
           <span class="number">
            39
           </span>
           )
          </li>
          <li class="">
           <span class="number">
            3
           </span>
           LOAD_GLOBAL
           <span class="number">
            0
           </span>
           (range)
          </li>
          <li class="alt">
           <span class="number">
            6
           </span>
           LOAD_CONST
           <span class="number">
            1
           </span>
           (
           <span class="number">
            10
           </span>
           )
          </li>
          <li class="">
           <span class="number">
            9
           </span>
           CALL_FUNCTION
           <span class="number">
            1
           </span>
           (
           <span class="number">
            1
           </span>
           positional,
           <span class="number">
            0
           </span>
           keyword pair)
          </li>
          <li class="alt">
           <span class="number">
            12
           </span>
           GET_ITER
          </li>
          <li class="">
           &gt;&gt;
           <span class="number">
            13
           </span>
           FOR_ITER
           <span class="number">
            22
           </span>
           (to
           <span class="number">
            38
           </span>
           )
          </li>
          <li class="alt">
           <span class="number">
            16
           </span>
           STORE_FAST
           <span class="number">
            1
           </span>
           (i)
          </li>
          <li class="">
          </li>
          <li class="alt">
           <span class="number">
            102
           </span>
           <span class="number">
            19
           </span>
           LOAD_FAST
           <span class="number">
            0
           </span>
           (x)
          </li>
          <li class="">
           <span class="number">
            22
           </span>
           LOAD_FAST
           <span class="number">
            1
           </span>
           (i)
          </li>
          <li class="alt">
           <span class="number">
            25
           </span>
           COMPARE_OP
           <span class="number">
            2
           </span>
           (==)
          </li>
          <li class="">
           <span class="number">
            28
           </span>
           POP_JUMP_IF_FALSE
           <span class="number">
            13
           </span>
          </li>
          <li class="alt">
          </li>
          <li class="">
           <span class="number">
            103
           </span>
           <span class="number">
            31
           </span>
           BREAK_LOOP
          </li>
          <li class="alt">
           <span class="number">
            32
           </span>
           JUMP_ABSOLUTE
           <span class="number">
            13
           </span>
          </li>
          <li class="">
           <span class="number">
            35
           </span>
           JUMP_ABSOLUTE
           <span class="number">
            13
           </span>
          </li>
          <li class="alt">
           &gt;&gt;
           <span class="number">
            38
           </span>
           POP_BLOCK
          </li>
          <li class="">
           &gt;&gt;
           <span class="number">
            39
           </span>
           LOAD_CONST
           <span class="number">
            0
           </span>
           (
           <span class="special">
            None
           </span>
           )
          </li>
          <li class="alt">
           <span class="number">
            42
           </span>
           RETURN_VALUE
          </li>
          <li class="">
           &gt;&gt;&gt; lol.__code__ = transform_code(lol.__code__, add_debug_op_everywhere, add_stacksize=
           <span class="number">
            3
           </span>
           )
          </li>
          <li class="alt">
          </li>
          <li class="">
          </li>
          <li class="alt">
           &gt;&gt;&gt; dis.dis(lol)
          </li>
          <li class="">
           <span class="number">
            101
           </span>
           <span class="number">
            0
           </span>
           &lt;
           <span class="number">
            0
           </span>
           &gt;
          </li>
          <li class="alt">
           <span class="number">
            1
           </span>
           SETUP_LOOP
           <span class="number">
            50
           </span>
           (to
           <span class="number">
            54
           </span>
           )
          </li>
          <li class="">
           <span class="number">
            4
           </span>
           &lt;
           <span class="number">
            0
           </span>
           &gt;
          </li>
          <li class="alt">
           <span class="number">
            5
           </span>
           LOAD_GLOBAL
           <span class="number">
            0
           </span>
           (range)
          </li>
          <li class="">
           <span class="number">
            8
           </span>
           &lt;
           <span class="number">
            0
           </span>
           &gt;
          </li>
          <li class="alt">
           <span class="number">
            9
           </span>
           LOAD_CONST
           <span class="number">
            1
           </span>
           (
           <span class="number">
            10
           </span>
           )
          </li>
          <li class="">
           <span class="number">
            12
           </span>
           &lt;
           <span class="number">
            0
           </span>
           &gt;
          </li>
          <li class="alt">
           <span class="number">
            13
           </span>
           CALL_FUNCTION
           <span class="number">
            1
           </span>
           (
           <span class="number">
            1
           </span>
           positional,
           <span class="number">
            0
           </span>
           keyword pair)
          </li>
          <li class="">
           <span class="number">
            16
           </span>
           &lt;
           <span class="number">
            0
           </span>
           &gt;
          </li>
          <li class="alt">
           <span class="number">
            17
           </span>
           GET_ITER
          </li>
          <li class="">
           &gt;&gt;
           <span class="number">
            18
           </span>
           &lt;
           <span class="number">
            0
           </span>
           &gt;
          </li>
          <li class="alt">
          </li>
          <li class="">
           <span class="number">
            102
           </span>
           <span class="number">
            19
           </span>
           FOR_ITER
           <span class="number">
            30
           </span>
           (to
           <span class="number">
            52
           </span>
           )
          </li>
          <li class="alt">
           <span class="number">
            22
           </span>
           &lt;
           <span class="number">
            0
           </span>
           &gt;
          </li>
          <li class="">
           <span class="number">
            23
           </span>
           STORE_FAST
           <span class="number">
            1
           </span>
           (i)
          </li>
          <li class="alt">
           <span class="number">
            26
           </span>
           &lt;
           <span class="number">
            0
           </span>
           &gt;
          </li>
          <li class="">
           <span class="number">
            27
           </span>
           LOAD_FAST
           <span class="number">
            0
           </span>
           (x)
          </li>
          <li class="alt">
           <span class="number">
            30
           </span>
           &lt;
           <span class="number">
            0
           </span>
           &gt;
          </li>
          <li class="">
          </li>
          <li class="alt">
           <span class="number">
            103
           </span>
           <span class="number">
            31
           </span>
           LOAD_FAST
           <span class="number">
            1
           </span>
           (i)
          </li>
          <li class="">
           <span class="number">
            34
           </span>
           &lt;
           <span class="number">
            0
           </span>
           &gt;
          </li>
          <li class="alt">
           <span class="number">
            35
           </span>
           COMPARE_OP
           <span class="number">
            2
           </span>
           (==)
          </li>
          <li class="">
           <span class="number">
            38
           </span>
           &lt;
           <span class="number">
            0
           </span>
           &gt;
          </li>
          <li class="alt">
           <span class="number">
            39
           </span>
           POP_JUMP_IF_FALSE
           <span class="number">
            18
           </span>
          </li>
          <li class="">
           <span class="number">
            42
           </span>
           &lt;
           <span class="number">
            0
           </span>
           &gt;
          </li>
          <li class="alt">
           <span class="number">
            43
           </span>
           BREAK_LOOP
          </li>
          <li class="">
           <span class="number">
            44
           </span>
           &lt;
           <span class="number">
            0
           </span>
           &gt;
          </li>
          <li class="alt">
           <span class="number">
            45
           </span>
           JUMP_ABSOLUTE
           <span class="number">
            18
           </span>
          </li>
          <li class="">
           <span class="number">
            48
           </span>
           &lt;
           <span class="number">
            0
           </span>
           &gt;
          </li>
          <li class="alt">
           <span class="number">
            49
           </span>
           JUMP_ABSOLUTE
           <span class="number">
            18
           </span>
          </li>
          <li class="">
           &gt;&gt;
           <span class="number">
            52
           </span>
           &lt;
           <span class="number">
            0
           </span>
           &gt;
          </li>
          <li class="alt">
           <span class="number">
            53
           </span>
           POP_BLOCK
          </li>
          <li class="">
           &gt;&gt;
           <span class="number">
            54
           </span>
           &lt;
           <span class="number">
            0
           </span>
           &gt;
          </li>
          <li class="alt">
           <span class="number">
            55
           </span>
           LOAD_CONST
           <span class="number">
            0
           </span>
           (
           <span class="special">
            None
           </span>
           )
          </li>
          <li class="">
           <span class="number">
            58
           </span>
           &lt;
           <span class="number">
            0
           </span>
           &gt;
          </li>
          <li class="alt">
           <span class="number">
            59
           </span>
           RETURN_VALUE
          </li>
          <li class="">
          </li>
          <li class="alt">
           <span class="comment">
            # Setup the simplest handler EVER
           </span>
          </li>
          <li class="">
           &gt;&gt;&gt;
           <span class="keyword">
            def
           </span>
           op_target(stack, frame):
          </li>
          <li class="alt">
           …
           <span class="keyword">
            print
           </span>
           (stack)
          </li>
          <li class="">
          </li>
          <li class="alt">
           <span class="comment">
            # GO
           </span>
          </li>
          <li class="">
           &gt;&gt;&gt; lol(
           <span class="number">
            2
           </span>
           )
          </li>
          <li class="alt">
           []
          </li>
          <li class="">
           []
          </li>
          <li class="alt">
           [&lt;
           <span class="keyword">
            class
           </span>
           <span class="string">
            ‘range’
           </span>
           &gt;]
          </li>
          <li class="">
           [
           <span class="number">
            10
           </span>
           , &lt;
           <span class="keyword">
            class
           </span>
           <span class="string">
            ‘range’
           </span>
           &gt;]
          </li>
          <li class="alt">
           [range(
           <span class="number">
            0
           </span>
           ,
           <span class="number">
            10
           </span>
           )]
          </li>
          <li class="">
           [&lt;range_iterator object at
           <span class="number">
            0x7f1349afab80
           </span>
           &gt;]
          </li>
          <li class="alt">
           [
           <span class="number">
            0
           </span>
           , &lt;range_iterator object at
           <span class="number">
            0x7f1349afab80
           </span>
           &gt;]
          </li>
          <li class="">
           [&lt;range_iterator object at
           <span class="number">
            0x7f1349afab80
           </span>
           &gt;]
          </li>
          <li class="alt">
           [
           <span class="number">
            2
           </span>
           , &lt;range_iterator object at
           <span class="number">
            0x7f1349afab80
           </span>
           &gt;]
          </li>
          <li class="">
           [
           <span class="number">
            0
           </span>
           ,
           <span class="number">
            2
           </span>
           , &lt;range_iterator object at
           <span class="number">
            0x7f1349afab80
           </span>
           &gt;]
          </li>
          <li class="alt">
           [
           <span class="special">
            False
           </span>
           , &lt;range_iterator object at
           <span class="number">
            0x7f1349afab80
           </span>
           &gt;]
          </li>
          <li class="">
           [&lt;range_iterator object at
           <span class="number">
            0x7f1349afab80
           </span>
           &gt;]
          </li>
          <li class="alt">
           [
           <span class="number">
            1
           </span>
           , &lt;range_iterator object at
           <span class="number">
            0x7f1349afab80
           </span>
           &gt;]
          </li>
          <li class="">
           [&lt;range_iterator object at
           <span class="number">
            0x7f1349afab80
           </span>
           &gt;]
          </li>
          <li class="alt">
           [
           <span class="number">
            2
           </span>
           , &lt;range_iterator object at
           <span class="number">
            0x7f1349afab80
           </span>
           &gt;]
          </li>
          <li class="">
           [
           <span class="number">
            1
           </span>
           ,
           <span class="number">
            2
           </span>
           , &lt;range_iterator object at
           <span class="number">
            0x7f1349afab80
           </span>
           &gt;]
          </li>
          <li class="alt">
           [
           <span class="special">
            False
           </span>
           , &lt;range_iterator object at
           <span class="number">
            0x7f1349afab80
           </span>
           &gt;]
          </li>
          <li class="">
           [&lt;range_iterator object at
           <span class="number">
            0x7f1349afab80
           </span>
           &gt;]
          </li>
          <li class="alt">
           [
           <span class="number">
            2
           </span>
           , &lt;range_iterator object at
           <span class="number">
            0x7f1349afab80
           </span>
           &gt;]
          </li>
          <li class="">
           [&lt;range_iterator object at
           <span class="number">
            0x7f1349afab80
           </span>
           &gt;]
          </li>
          <li class="alt">
           [
           <span class="number">
            2
           </span>
           , &lt;range_iterator object at
           <span class="number">
            0x7f1349afab80
           </span>
           &gt;]
          </li>
          <li class="">
           [
           <span class="number">
            2
           </span>
           ,
           <span class="number">
            2
           </span>
           , &lt;range_iterator object at
           <span class="number">
            0x7f1349afab80
           </span>
           &gt;]
          </li>
          <li class="alt">
           [
           <span class="special">
            True
           </span>
           , &lt;range_iterator object at
           <span class="number">
            0x7f1349afab80
           </span>
           &gt;]
          </li>
          <li class="">
           [&lt;range_iterator object at
           <span class="number">
            0x7f1349afab80
           </span>
           &gt;]
          </li>
          <li class="alt">
           []
          </li>
          <li class="">
           [
           <span class="special">
            None
           </span>
           ]
          </li>
         </ol>
        </div>
       </blockquote>
       <p>
        甚好！现在我们知道了如何获取堆栈信息和 Python 中每一个操作对应的帧信息。上面结果所展示的结果目前而言并不是很实用。在最后一部分中让我们对注入做进一步的封装。
       </p>
       <h3>
        增加 Python 封装
       </h3>
       <p>
        正如您所见到的，所有的底层接口都是好用的。我们最后要做的一件事是让 op_target 更加方便使用(这部分相对而言比较空泛一些，毕竟在我看来这不是整个项目中最有趣的部分)。
       </p>
       <p>
        首先我们来看一下帧的参数所能提供的信息，如下所示：
       </p>
       <ul>
        <li>
         <code>
          f_code
         </code>
         当前帧将执行的 code object
        </li>
        <li>
         <code>
          f_lasti
         </code>
         当前的操作(code object 中的字节码字符串的索引)
        </li>
       </ul>
       <p>
        经过我们的处理我们可以得知
        <code>
         DEBUG_OP
        </code>
        之后要被执行的操作码，这对我们聚合数据并展示是相当有用的。
       </p>
       <p>
        新建一个用于追踪函数内部机制的类：
       </p>
       <ul>
        <li>
         改变函数自身的
         <code>
          co_code
         </code>
        </li>
        <li>
         设置回调函数作为
         <code>
          op_debug
         </code>
         的目标函数
        </li>
       </ul>
       <p>
        一旦我们知道下一个操作，我们就可以分析它并修改它的参数。举例来说我们可以增加一个
        <code>
         auto-follow-called-functions
        </code>
        的特性。
       </p>
       <blockquote>
        <div class="dp-highlighter bg_py">
         <div class="bar">
          <div class="tools">
           <b>
            [py]
           </b>
          </div>
         </div>
         <ol class="dp-py" start="1">
          <li class="alt">
           <span class="keyword">
            def
           </span>
           op_target(l, f, exc=
           <span class="special">
            None
           </span>
           ):
          </li>
          <li class="">
           <span class="keyword">
            if
           </span>
           op_target.callback
           <span class="keyword">
            is
           </span>
           <span class="keyword">
            not
           </span>
           <span class="special">
            None
           </span>
           :
          </li>
          <li class="alt">
           op_target.callback(l, f, exc)
          </li>
          <li class="">
          </li>
          <li class="alt">
           <span class="keyword">
            class
           </span>
           Trace:
          </li>
          <li class="">
           <span class="keyword">
            def
           </span>
           __init__(
           <span class="special">
            self
           </span>
           , func):
          </li>
          <li class="alt">
           <span class="special">
            self
           </span>
           .func = func
          </li>
          <li class="">
          </li>
          <li class="alt">
           <span class="keyword">
            def
           </span>
           call(
           <span class="special">
            self
           </span>
           , *args, **kwargs):
          </li>
          <li class="">
           <span class="special">
            self
           </span>
           .add_func_to_trace(
           <span class="special">
            self
           </span>
           .func)
          </li>
          <li class="alt">
           <span class="comment">
            # Activate Trace callback for the func call
           </span>
          </li>
          <li class="">
           op_target.callback =
           <span class="special">
            self
           </span>
           .callback
          </li>
          <li class="alt">
           <span class="keyword">
            try
           </span>
           :
          </li>
          <li class="">
           res =
           <span class="special">
            self
           </span>
           .func(*args, **kwargs)
          </li>
          <li class="alt">
           <span class="keyword">
            except
           </span>
           Exception as e:
          </li>
          <li class="">
           res = e
          </li>
          <li class="alt">
           op_target.callback =
           <span class="special">
            None
           </span>
          </li>
          <li class="">
           <span class="keyword">
            return
           </span>
           res
          </li>
          <li class="alt">
          </li>
          <li class="">
           <span class="keyword">
            def
           </span>
           add_func_to_trace(
           <span class="special">
            self
           </span>
           , f):
          </li>
          <li class="alt">
           <span class="comment">
            # Is it code? is it already transformed?
           </span>
          </li>
          <li class="">
           <span class="keyword">
            if
           </span>
           <span class="keyword">
            not
           </span>
           hasattr(f ,
           <span class="string">
            “op_debug”
           </span>
           )
           <span class="keyword">
            and
           </span>
           hasattr(f,
           <span class="string">
            “__code__”
           </span>
           ):
          </li>
          <li class="alt">
           f.__code__ = transform_code(f.__code__, transform=add_everywhere, add_stacksize=ADD_STACK)
          </li>
          <li class="">
           f.__globals__[
           <span class="string">
            ‘op_target’
           </span>
           ] = op_target
          </li>
          <li class="alt">
           f.op_debug =
           <span class="special">
            True
           </span>
          </li>
          <li class="">
          </li>
          <li class="alt">
           <span class="keyword">
            def
           </span>
           do_auto_follow(
           <span class="special">
            self
           </span>
           , stack, frame):
          </li>
          <li class="">
           <span class="comment">
            # Nothing fancy: FrameAnalyser is just the wrapper that gives the next executed instruction
           </span>
          </li>
          <li class="alt">
           next_instr = FrameAnalyser(frame).next_instr()
          </li>
          <li class="">
           <span class="keyword">
            if
           </span>
           <span class="string">
            “CALL”
           </span>
           <span class="keyword">
            in
           </span>
           next_instr.opname:
          </li>
          <li class="alt">
           arg = next_instr.arg
          </li>
          <li class="">
           f_index = (arg &amp;
           <span class="number">
            0xff
           </span>
           ) + (
           <span class="number">
            2
           </span>
           * (arg &gt;&gt;
           <span class="number">
            8
           </span>
           ))
          </li>
          <li class="alt">
           called_func = stack[f_index]
          </li>
          <li class="">
          </li>
          <li class="alt">
           <span class="comment">
            # If call target is not traced yet: do it
           </span>
          </li>
          <li class="">
           <span class="keyword">
            if
           </span>
           <span class="keyword">
            not
           </span>
           hasattr(called_func,
           <span class="string">
            “op_debug”
           </span>
           ):
          </li>
          <li class="alt">
           <span class="special">
            self
           </span>
           .add_func_to_trace(called_func)
          </li>
         </ol>
        </div>
       </blockquote>
       <p>
        现在我们实现一个 Trace 的子类，在这个子类中增加 callback 和 do
        <em>
         report 这两个方法。callback 方法将在每一个操作之后被调用。do
        </em>
        report 方法将我们收集到的信息打印出来。
       </p>
       <p>
        这是一个伪函数追踪器实现：
       </p>
       <blockquote>
        <div class="dp-highlighter bg_py">
         <div class="bar">
          <div class="tools">
           <b>
            [py]
           </b>
          </div>
         </div>
         <ol class="dp-py" start="1">
          <li class="alt">
           <span class="keyword">
            class
           </span>
           DummyTrace(Trace):
          </li>
          <li class="">
           <span class="keyword">
            def
           </span>
           __init__(
           <span class="special">
            self
           </span>
           , func):
          </li>
          <li class="alt">
           <span class="special">
            self
           </span>
           .func = func
          </li>
          <li class="">
           <span class="special">
            self
           </span>
           .data = collections.OrderedDict()
          </li>
          <li class="alt">
           <span class="special">
            self
           </span>
           .last_frame =
           <span class="special">
            None
           </span>
          </li>
          <li class="">
           <span class="special">
            self
           </span>
           .known_frame = []
          </li>
          <li class="alt">
           <span class="special">
            self
           </span>
           .report = []
          </li>
          <li class="">
          </li>
          <li class="alt">
           <span class="keyword">
            def
           </span>
           callback(
           <span class="special">
            self
           </span>
           , stack, frame, exc):
          </li>
          <li class="">
           <span class="keyword">
            if
           </span>
           frame
           <span class="keyword">
            not
           </span>
           <span class="keyword">
            in
           </span>
           <span class="special">
            self
           </span>
           .known_frame:
          </li>
          <li class="alt">
           <span class="special">
            self
           </span>
           .known_frame.append(frame)
          </li>
          <li class="">
           <span class="special">
            self
           </span>
           .report.append(
           <span class="string">
            ” === Entering New Frame {0} ({1}) ===”
           </span>
           .format(frame.f_code.co_name, id(frame)))
          </li>
          <li class="alt">
           <span class="special">
            self
           </span>
           .last_frame = frame
          </li>
          <li class="">
           <span class="keyword">
            if
           </span>
           frame !=
           <span class="special">
            self
           </span>
           .last_frame:
          </li>
          <li class="alt">
           <span class="special">
            self
           </span>
           .report.append(
           <span class="string">
            ” === Returning to Frame {0} {1}===”
           </span>
           .format(frame.f_code.co_name, id(frame)))
          </li>
          <li class="">
           <span class="special">
            self
           </span>
           .last_frame = frame
          </li>
          <li class="alt">
          </li>
          <li class="">
           <span class="special">
            self
           </span>
           .report.append(str(stack))
          </li>
          <li class="alt">
           instr = FrameAnalyser(frame).next_instr()
          </li>
          <li class="">
           offset = str(instr.offset).rjust(
           <span class="number">
            8
           </span>
           )
          </li>
          <li class="alt">
           opname = str(instr.opname).ljust(
           <span class="number">
            20
           </span>
           )
          </li>
          <li class="">
           arg = str(instr.arg).ljust(
           <span class="number">
            10
           </span>
           )
          </li>
          <li class="alt">
           <span class="special">
            self
           </span>
           .report.append(
           <span class="string">
            “{0}  {1} {2} {3}”
           </span>
           .format(offset, opname, arg, instr.argval))
          </li>
          <li class="">
           <span class="special">
            self
           </span>
           .do_auto_follow(stack, frame)
          </li>
          <li class="alt">
          </li>
          <li class="">
           <span class="keyword">
            def
           </span>
           do_report(
           <span class="special">
            self
           </span>
           ):
          </li>
          <li class="alt">
           <span class="keyword">
            print
           </span>
           (
           <span class="string">
            “\n”
           </span>
           .join(
           <span class="special">
            self
           </span>
           .report))
          </li>
         </ol>
        </div>
       </blockquote>
       <p>
        这里有一些实现的例子和使用方法。格式有些不方便观看，毕竟我并不擅长于搞这种对用户友好的报告的事儿。
       </p>
       <ul>
        <li>
         <a href="http://hakril.net/python/article/example_dummy_trace.html">
          例1
         </a>
         自动追踪堆栈信息和已经执行的指令
        </li>
        <li>
         <a href="http://hakril.net/python/article/example_dummy_contextmanager.html">
          例2
         </a>
         上下文管理
        </li>
       </ul>
       <p>
        递推式构造列表(List Comprehensions)的追踪示例 。
       </p>
       <ul>
        <li>
         <a href="http://hakril.net/python/article/example_list_comp_dummy.html">
          例3
         </a>
         伪追踪器的输出
        </li>
        <li>
         <a href="http://hakril.net/python/article/example_list_comp_stacktrace.html">
          例4
         </a>
         输出收集的堆栈信息
        </li>
       </ul>
       <h3>
        总结
       </h3>
       <p>
        这个小项目是一个了解 Python 底层的良好途径，包括解释器的 main loop，Python 实现的 C 代码编程、Python 字节码。通过这个小工具我们可以看到 Python 一些有趣构造函数的字节码行为，例如生成器、上下文管理和递推式构造列表。
       </p>
       <p>
        <a href="http://hakril.net/blog_data/assembly_tracer_code.zip">
         这里
        </a>
        是这个小项目的完整代码。更进一步的，我们还可以做的是修改我们所追踪的函数的堆栈。我虽然不确定这个是否有用，但是可以肯定是这一过程是相当有趣的。
       </p>
       <p>
        作者
        <span class="ago">
         Hakril
        </span>
       </p>
       <p>
        <b>
         原文链接：
        </b>
        <a href="http://blog.hakril.net/articles/2-understanding-python-execution-tracer.html">
         Understanding Python execution from inside: A Python assembly tracer
        </a>
       </p>
      </div>
      <div>
       <strong>
        注：转载文章均来自于公开网络，仅供学习使用，不会用于任何商业用途，如果侵犯到原作者的权益，请您与我们联系删除或者授权事宜，联系邮箱：contact@dataunion.org。转载数盟网站文章请注明原文章作者，否则产生的任何版权纠纷与数盟无关。
       </strong>
      </div>
      <!--content_text-->
      <div class="fenxian">
       <!-- JiaThis Button BEGIN -->
       <div class="jiathis_style_32x32">
        <p class="jiathis_button_weixin">
        </p>
        <p class="jiathis_button_tsina">
        </p>
        <p class="jiathis_button_qzone">
        </p>
        <p class="jiathis_button_cqq">
        </p>
        <p class="jiathis_button_tumblr">
        </p>
        <a class="jiathis jiathis_txt jtico jtico_jiathis" href="http://www.jiathis.com/share" target="_blank">
        </a>
        <p class="jiathis_counter_style">
        </p>
       </div>
       <!-- JiaThis Button END -->
      </div>
     </article>
     <!--content-->
     <!--相关文章-->
     <div class="xianguan">
      <div class="xianguantitle">
       相关文章！
      </div>
      <ul class="pic">
       <li>
        <a href="http://dataunion.org/20824.html">
         <img src="http://dataunion.org/wp-content/uploads/2015/09/t018630756a7e263b33-300x165.jpg"/>
        </a>
        <a class="link" href="http://dataunion.org/20824.html" rel="bookmark" title="如何判断一笔交易是否属于欺诈？你只是需要一点数据挖掘">
         如何判断一笔交易是否属于欺诈？你只是需要一点数据挖掘
        </a>
       </li>
       <li>
        <a href="http://dataunion.org/20820.html">
         <img src="http://dataunion.org/wp-content/uploads/2015/09/1-300x200.jpg"/>
        </a>
        <a class="link" href="http://dataunion.org/20820.html" rel="bookmark" title="人们对Python在企业级开发中的10大误解">
         人们对Python在企业级开发中的10大误解
        </a>
       </li>
       <li>
        <a href="http://dataunion.org/20811.html">
         <img src="http://dataunion.org/wp-content/uploads/2015/09/t0133fcacae8523307b_副本-300x200.jpg"/>
        </a>
        <a class="link" href="http://dataunion.org/20811.html" rel="bookmark" title="大神亲传：26条深度学习的金科玉律！">
         大神亲传：26条深度学习的金科玉律！
        </a>
       </li>
       <li>
        <a href="http://dataunion.org/20808.html">
         <img src="http://dataunion.org/wp-content/uploads/2015/09/640.webp-11-300x137.jpg"/>
        </a>
        <a class="link" href="http://dataunion.org/20808.html" rel="bookmark" title="我们是如何在一张地图上表现86万个数据的">
         我们是如何在一张地图上表现86万个数据的
        </a>
       </li>
      </ul>
     </div>
     <!--相关文章-->
     <div class="comment" id="comments">
      <!-- You can start editing here. -->
      <!-- If comments are open, but there are no comments. -->
      <div class="title">
       期待你一针见血的评论，Come on！
      </div>
      <div id="respond">
       <p>
        不用想啦，马上
        <a href="http://dataunion.org/wp-login.php?redirect_to=http%3A%2F%2Fdataunion.org%2F19079.html">
         "登录"
        </a>
        发表自已的想法.
       </p>
      </div>
     </div>
     <!-- .nav-single -->
    </div>
    <!--Container End-->
    <aside id="sitebar">
     <div class="sitebar_list2">
      <div class="wptag">
       <span class="tagtitle">
        热门标签+
       </span>
       <div class="tagg">
        <ul class="menu" id="menu-%e5%8f%8b%e6%83%85%e9%93%be%e6%8e%a5">
         <li class="menu-item menu-item-type-custom menu-item-object-custom menu-item-1605" id="menu-item-1605">
          <a href="http://taidizh.com/">
           泰迪智慧
          </a>
         </li>
         <li class="menu-item menu-item-type-custom menu-item-object-custom menu-item-20884" id="menu-item-20884">
          <a href="http://www.transwarp.cn/">
           星环科技
          </a>
         </li>
         <li class="menu-item menu-item-type-custom menu-item-object-custom menu-item-3538" id="menu-item-3538">
          <a href="http://datall.org/">
           珈和遥感
          </a>
         </li>
         <li class="menu-item menu-item-type-custom menu-item-object-custom menu-item-20888" id="menu-item-20888">
          <a href="http://www.chinahadoop.cn/">
           小象学院
          </a>
         </li>
        </ul>
       </div>
      </div>
     </div>
     <div class="sitebar_list">
      <div class="textwidget">
       <div align="center">
        <a href="http://study.163.com/course/courseMain.htm?courseId=991022" target="_blank">
         <img src="http://dataunion.org/wp-content/uploads/2016/03/dv.jpg"/>
        </a>
       </div>
      </div>
     </div>
     <div class="sitebar_list">
      <h4 class="sitebar_title">
       文章分类
      </h4>
      <div class="tagcloud">
       <a class="tag-link-44" href="http://dataunion.org/category/industry/demo" style="font-size: 10.204724409449pt;" title="4个话题">
        Demo展示
       </a>
       <a class="tag-link-31" href="http://dataunion.org/category/experts" style="font-size: 15.826771653543pt;" title="52个话题">
        专家团队
       </a>
       <a class="tag-link-870" href="http://dataunion.org/category/tech/ai" style="font-size: 19.795275590551pt;" title="273个话题">
        人工智能
       </a>
       <a class="tag-link-488" href="http://dataunion.org/category/%e5%8a%a0%e5%85%a5%e6%95%b0%e7%9b%9f" style="font-size: 8pt;" title="1个话题">
        加入数盟
       </a>
       <a class="tag-link-869" href="http://dataunion.org/category/tech/viz" style="font-size: 17.204724409449pt;" title="93个话题">
        可视化
       </a>
       <a class="tag-link-30" href="http://dataunion.org/category/partners" style="font-size: 10.645669291339pt;" title="5个话题">
        合作伙伴
       </a>
       <a class="tag-link-889" href="http://dataunion.org/category/parterc" style="font-size: 11.582677165354pt;" title="8个话题">
        合作会议
       </a>
       <a class="tag-link-104" href="http://dataunion.org/category/books" style="font-size: 12.96062992126pt;" title="15个话题">
        图书
       </a>
       <a class="tag-link-220" href="http://dataunion.org/category/tech/base" style="font-size: 19.850393700787pt;" title="281个话题">
        基础架构
       </a>
       <a class="tag-link-219" href="http://dataunion.org/category/tech/analysis" style="font-size: 19.409448818898pt;" title="232个话题">
        数据分析
       </a>
       <a class="tag-link-887" href="http://dataunion.org/category/tech/dm" style="font-size: 13.291338582677pt;" title="17个话题">
        数据挖掘
       </a>
       <a class="tag-link-34" href="http://dataunion.org/category/tech" style="font-size: 20.732283464567pt;" title="404个话题">
        文章
       </a>
       <a class="tag-link-1" href="http://dataunion.org/category/uncategorized" style="font-size: 22pt;" title="693个话题">
        未分类
       </a>
       <a class="tag-link-4" href="http://dataunion.org/category/events" style="font-size: 14.503937007874pt;" title="29个话题">
        活动
       </a>
       <a class="tag-link-890" href="http://dataunion.org/category/tech/%e6%b7%b1%e5%ba%a6%e5%ad%a6%e4%b9%a0" style="font-size: 10.204724409449pt;" title="4个话题">
        深度学习
       </a>
       <a class="tag-link-221" href="http://dataunion.org/category/tech/devl" style="font-size: 18.968503937008pt;" title="193个话题">
        编程语言
       </a>
       <a class="tag-link-888" href="http://dataunion.org/category/career" style="font-size: 15.661417322835pt;" title="48个话题">
        职业规划
       </a>
       <a class="tag-link-5" href="http://dataunion.org/category/jobs" style="font-size: 14.11811023622pt;" title="25个话题">
        职位
       </a>
       <a class="tag-link-871" href="http://dataunion.org/category/industry" style="font-size: 15.716535433071pt;" title="49个话题">
        行业
       </a>
       <a class="tag-link-613" href="http://dataunion.org/category/industry/case" style="font-size: 16.984251968504pt;" title="84个话题">
        行业应用
       </a>
       <a class="tag-link-885" href="http://dataunion.org/category/industry/news" style="font-size: 17.425196850394pt;" title="102个话题">
        行业资讯
       </a>
       <a class="tag-link-10" href="http://dataunion.org/category/training" style="font-size: 14.228346456693pt;" title="26个话题">
        课程
       </a>
       <a class="tag-link-16" href="http://dataunion.org/category/sources" style="font-size: 15.661417322835pt;" title="48个话题">
        资源
       </a>
      </div>
     </div>
     <div class="sitebar_list">
      <h4 class="sitebar_title">
       功能
      </h4>
      <ul>
       <li>
        <a href="http://dataunion.org/wp-login.php?action=register">
         注册
        </a>
       </li>
       <li>
        <a href="http://dataunion.org/wp-login.php">
         登录
        </a>
       </li>
       <li>
        <a href="http://dataunion.org/feed">
         文章
         <abbr title="Really Simple Syndication">
          RSS
         </abbr>
        </a>
       </li>
       <li>
        <a href="http://dataunion.org/comments/feed">
         评论
         <abbr title="Really Simple Syndication">
          RSS
         </abbr>
        </a>
       </li>
       <li>
        <a href="https://cn.wordpress.org/" title="基于WordPress，一个优美、先进的个人信息发布平台。">
         WordPress.org
        </a>
       </li>
      </ul>
     </div>
    </aside>
    <div class="clear">
    </div>
   </div>
   <!--main-->
   ﻿
   <footer id="dibu">
    <div class="about">
     <div class="right">
      <ul class="menu" id="menu-%e5%ba%95%e9%83%a8%e8%8f%9c%e5%8d%95">
       <li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-18024" id="menu-item-18024">
        <a href="http://dataunion.org/category/partners">
         合作伙伴
        </a>
       </li>
       <li class="menu-item menu-item-type-post_type menu-item-object-page menu-item-20881" id="menu-item-20881">
        <a href="http://dataunion.org/contribute">
         文章投稿
        </a>
       </li>
       <li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-20872" id="menu-item-20872">
        <a href="http://dataunion.org/category/%e5%8a%a0%e5%85%a5%e6%95%b0%e7%9b%9f">
         加入数盟
        </a>
       </li>
       <li class="menu-item menu-item-type-post_type menu-item-object-page menu-item-22441" id="menu-item-22441">
        <a href="http://dataunion.org/f-links">
         友情链接
        </a>
       </li>
       <li class="menu-item menu-item-type-post_type menu-item-object-page menu-item-20874" id="menu-item-20874">
        <a href="http://dataunion.org/aboutus">
         关于数盟
        </a>
       </li>
      </ul>
      <p class="banquan">
       数盟社区        ，
        做最棒的数据科学社区
      </p>
     </div>
     <div class="left">
      <ul class="bottomlist">
       <li>
        <a href="http://weibo.com/DataScientistUnion  " target="_blank" 　title="">
         <img src="http://dataunion.org/wp-content/themes/yzipi/images/weibo.png"/>
        </a>
       </li>
       <li>
        <a class="cd-popup-trigger" href="http://dataunion.org/19079.html#0">
         <img src="http://dataunion.org/wp-content/themes/yzipi/images/weixin.png"/>
        </a>
       </li>
      </ul>
      <div class="cd-popup">
       <div class="cd-popup-container">
        <h1>
         扫描二维码,加微信公众号
        </h1>
        <img src="http://dataunion.org/wp-content/themes/yzipi/images/2014-12-06-1515289049.png"/>
        <a class="cd-popup-close" href="http://dataunion.org/19079.html">
        </a>
       </div>
       <!-- cd-popup-container -->
      </div>
      <!-- cd-popup -->
     </div>
    </div>
    <!--about-->
    <div class="bottom">
     <a href="http://dataunion.org/">
      数盟社区
     </a>
     <a href="http://www.miitbeian.gov.cn/" rel="external nofollow" target="_blank">
      京ICP备14026740号
     </a>
     联系我们：
     <a href="mailto:contact@dataunion.org" target="_blank">
      contact@dataunion.org
     </a>
     <div class="tongji">
     </div>
     <!--bottom-->
     <div class="scroll" id="scroll" style="display:none;">
      ︿
     </div>
    </div>
   </footer>
   <!--dibu-->
  </div>
 </body>
</html>